package io.gitee.yxsnake.embark.web.core.configuration;


import com.google.common.collect.Lists;
import io.gitee.yxsnake.embark.web.core.configuration.properties.*;
import io.gitee.yxsnake.embark.web.core.constant.HeaderCons;
import io.gitee.yxsnake.embark.web.core.constant.StringPool;
import io.gitee.yxsnake.embark.web.core.framework.filter.MDCFilter;
import io.gitee.yxsnake.embark.web.core.framework.filter.XssFilter;
import io.gitee.yxsnake.embark.web.core.interceptor.RetrofitTraceIdInterceptor;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.DispatcherType;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import org.springframework.web.filter.CommonsRequestLoggingFilter;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;


/**
 * @author: snake
 * @create-time: 2024-09-29
 * @description:
 * @version: 1.0
 */
@Slf4j
@AllArgsConstructor
@Import({
        HealthProperties.class,
        WebCoreProperties.class,
        SwaggerProperties.class,
        PrintLogProperties.class,
        XssProperties.class,
        TraceProperties.class
})
@Configuration
public class WebCoreAutoConfiguration {

    private final TraceProperties trace;

    private final HealthProperties health;

    private final WebCoreProperties webCore;

    private final SwaggerProperties swagger;

    private final PrintLogProperties printLog;

    private final XssProperties xss;

    private final Environment environment;

    @PostConstruct
    public void init() {
        log.info("------------ embark-web-core-starter start information -----------");
        log.info("web-core-starter");
        log.info("  |-core");
        log.info("    |-applicationName: {}",getApplicationName(environment));
        log.info("    |-serverPort: {}",getServerPort(environment));
        log.info("  |-health");
        log.info("    |-enabled: {}", health.getEnabled());
        log.info("    |-path: {}", health.getPath());
        log.info("  |-swagger");
        log.info("    |-enabled: {}", swagger.getEnabled());
        log.info("  |-printLog");
        log.info("    |-enabled: {}", printLog.isEnabled());
        log.info("  |-xss");
        log.info("    |-enabled: {}", xss.getEnabled());
        log.info("  |-trace");
        log.info("    |-enabled: {}", trace.getEnabled());
        log.info("-------------------------------------------------------------");
    }

    private String getApplicationName(Environment environment){
        return environment.getProperty("spring.application.name");
    }

    private String getServerPort(Environment environment){
        return environment.getProperty("server.port");
    }

    @Bean
    @ConditionalOnMissingBean
    public WebCoreMvcAutoConfiguration webMvcConfiguration() {
        return new WebCoreMvcAutoConfiguration(webCore);
    }

    @Bean
    public Validator validator(AutowireCapableBeanFactory autowireCapableBeanFactory) {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // failFast=true 不校验所有参数，只要出现校验失败情况直接返回，不再进行后续参数校验
                .failFast(true)
                .constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
                .buildValidatorFactory();

        return validatorFactory.getValidator();
    }

    /**
     * xss 过滤器 注入
     * @description 注入 xxs过滤器
     *
     */
    @Bean
    public FilterRegistrationBean xssFilterRegistration() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        // 设置过滤器的分发类型为请求类型
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        // 创建XssFilter的实例
        registrationBean.setFilter(new XssFilter());
        // 添加过滤器需要拦截的URL模式，这些模式从配置文件中的"urlPatterns"属性读取
        List<String> urlPatternList = SplitUtil.split(xss.getUrlPatterns(), StringPool.COMMA);
        registrationBean.setUrlPatterns(urlPatternList);
        // 设置过滤器的名称
        registrationBean.setName("XssFilter");
        // 设置过滤器的执行顺序，数值越小，优先级越高
        registrationBean.setOrder(9999);
        // 创建一个Map，用于存储过滤器的初始化参数
        Map<String, String> initParameters = new HashMap<>();
        // 将配置文件中的"excludes"属性设置到过滤器的初始化参数中
        initParameters.put("excludes", xss.getExcludes());
        // 将配置文件中的"enabled"属性设置到过滤器的初始化参数中
        initParameters.put("enabled", xss.getEnabled().toString());
        // 将初始化参数设置到FilterRegistrationBean中
        registrationBean.setInitParameters(initParameters);
        // 返回FilterRegistrationBean，包含了XssFilter的配置信息
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean mdcFilterRegistration() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        // 设置过滤器的分发类型为请求类型
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        // 创建MDCFilter的实例
        registrationBean.setFilter(new MDCFilter());
        registrationBean.setUrlPatterns(Lists.newArrayList("/*"));
        // 设置过滤器的名称
        registrationBean.setName("MDCFilter");
        // 设置过滤器的执行顺序，数值越小，优先级越高
        registrationBean.setOrder(Integer.MIN_VALUE);
        return registrationBean;
    }

    @Bean
    @ConditionalOnProperty(value = "web-core.log.enabled", havingValue = "true", matchIfMissing = true)
    public CommonsRequestLoggingFilter loggingFilter(){

        CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
        // 是否记录请求的查询参数信息
        filter.setIncludeQueryString(true) ;
        // 是否记录请求body内容
        filter.setIncludePayload(true) ;
        // 是否记录请求header信息
        filter.setIncludeHeaders(true) ;
        // 是否记录请求客户端信息
        filter.setIncludeClientInfo(true) ;
        // 最大参数长度
        filter.setMaxPayloadLength(2000);
        // 设置日期记录的前缀
        filter.setAfterMessagePrefix("REQUEST DATA: ") ;
        Predicate<String> predicate = str -> HeaderCons.HEADER_AUTHORIZATION.equals(str)|| HeaderCons.TOKEN.equals(str);
        filter.setHeaderPredicate(predicate);
        return filter ;
    }

    @Bean
    public RetrofitTraceIdInterceptor retrofitTraceIdInterceptor(){
        return new RetrofitTraceIdInterceptor();
    }
}
